#pragma once

#include <functional>
#include <memory>
#include <string>
#include <unordered_map>

#include "coin2/exchange/base/market/market_exchange_api.h"
#include "naviasraw/fix/fixmsg.h"

namespace coin2::exchange::base::order {
struct OrderExecution;
}

namespace coin2::exchange::base::symbology {
class IProduct;
class IProductHolder;
}  // namespace coin2::exchange::base::symbology

namespace coin2::exchange::deribit::api_order::fix {

constexpr auto SPACE = ' ';
constexpr auto MARKET = '1';
constexpr auto LIMIT = '2';
constexpr auto STOP_LIMIT = '4';
constexpr auto BUY = '1';
constexpr auto SELL = '2';
constexpr auto GOOD_TILL_CANCEL = '1';
constexpr auto IMMEDIATE_OR_CANCEL = '3';
constexpr auto FILL_OR_KILL = '4';
constexpr auto POST_ONLY = 'P';
constexpr auto EMPTY = "";

using IProduct = coin2::exchange::base::symbology::IProduct;
using IProductHolder = coin2::exchange::base::symbology::IProductHolder;
using GetProductHolderFunc =
    std::function<const IProductHolder*(const IProduct& product, int64_t timestamp)>;

/********************************************************************************
 * struct DeribitOrder                                                              *
 ********************************************************************************/
struct DeribitOrder {
  using OrderExecution = coin2::exchange::base::order::OrderExecution;
  using MarketExchangeApi = coin2::exchange::base::market::MarketExchangeApi;

  std::string client_id;
  std::string symbol;
  char type;
  double quantity;
  double price;
  double cash_order_qty;
  double stop_price;
  char side;
  char time_in_force;
  std::string exec_inst;

  std::unordered_map<std::string, double> contract_value_map_;

  explicit DeribitOrder(
      const OrderExecution&,
      const MarketExchangeApi& mea,
      GetProductHolderFunc get_product_holder,
      double contract_multiplier);
  void Parse(
      const OrderExecution&,
      const MarketExchangeApi& mea,
      GetProductHolderFunc get_product_holder,
      double contract_multiplier);
  void Validate() const;
};

class FixMessageBuilder2 {
 public:
  using RequestTypePtr = std::unique_ptr<::naviasraw::fix::Request>;
  using OrderExecution = coin2::exchange::base::order::OrderExecution;
  using Request = ::naviasraw::fix::Request;

  explicit FixMessageBuilder2(
      const MarketExchangeApi& mea,
      const std::string& begin_string,
      const std::string& sender_comp_id,
      const std::string& target_comp_id);
  ~FixMessageBuilder2() = default;
  FixMessageBuilder2(const FixMessageBuilder2&) = delete;
  FixMessageBuilder2& operator=(const FixMessageBuilder2&) = delete;

  Request* GenerateLogon(
      char* buf,
      size_t size,
      const std::string& user_name,
      const std::string& secret_key,
      const std::string& cancel_orders_on_disconnect);
  Request* GenerateLogout(char* buf, size_t size);
  Request* GeneratePlaceOrder(
      char* buf,
      size_t size,
      const OrderExecution& ord_exec,
      GetProductHolderFunc get_product_holder,
      double contract_multiplier);
  Request* GenerateCancelOrder(char* buf, size_t size, const OrderExecution& ord_exec);
  Request* GenerateSecurityListRequest(char* buf, size_t size);

 protected:
  const MarketExchangeApi& mea_;
  std::string begin_string_;
  std::string sender_comp_id_;
  std::string target_comp_id_;

  Request logon_;
  Request logout_;
  Request limit_order_;
  Request cancel_;
  Request security_list_req_;
};

}  // namespace coin2::exchange::deribit::api_order::fix
